function updateEnityDirection(enity, hude) {
	const angle = 180 - Math.floor((hude * 180 / Math.PI) % 360);
	switch(true) {
		default: 
			enity.direction = 3;
			break;
		case angle > 45 && angle <= 135:
			enity.direction = 2;
			break;
		case angle > 135 && angle <= 225:
			enity.direction = 0;
			break;
		case angle > 225 && angle <= 315:
			enity.direction = 1;
			break;
	}
}

class Action {
}

class MoveTo extends Action {
	constructor(enity, path) {
		super();
		
		this.enity = enity;
		this.path = path;
	}
	
	update() {
		if(!this.path || this.path.length <= 0) {
			this.enity.action = null;
			this.enity.state = "idea";
			return;
		}
		
		// 计算速度
		const lastTarget = this.path[this.path.length - 1];
		const lastXDiff = lastTarget.x - this.enity.x;
		const lastYDiff = lastTarget.y - this.enity.y;
		const lastDistance = Math.sqrt(lastXDiff * lastXDiff + lastYDiff * lastYDiff);

		// 计算距离
		const target = this.path[0];
		const speed = this.enity.speed;
		const xDiff = target.x - this.enity.x;
		const yDiff = target.y - this.enity.y;
		const distance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
		if(distance > speed) { 
			const hude = xDiff == 0 && yDiff == 0 ? 0 : Math.atan2(xDiff, yDiff);
				   
			// 计算位置 
			Matter.Body.setVelocity(this.enity.body, {x:speed * Math.sin(hude),y:speed * Math.cos(hude)});
		}
		else { 
			//this.enity.x = target.x;
			//this.enity.y = target.y;
			this.path.splice(0,1);
		}			
	}
}

class TalkTo extends Action {
	constructor(enity, target) {
		super();
		
		this.enity = enity;
		this.target = target;
		this.moveAction = new MoveTo(enity, enity.scene.findPath(enity.position, target.position));
	}
	
	update() {
		// 检查与目标点距离
		const xDiff = this.target.x - this.enity.x;
		const yDiff = this.target.y - this.enity.y;
		const distance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
		
		// 如果小于2个格子则进行对话
		if(distance < this.enity.scene.gridSize * 2) {
			
			// 结束动作
			this.enity.action = null;
			this.enity.state = `idea`;
			
			// 执行NPC对话
			if(L2D.Bindings[this.target.uuid]) 
				L2D.Bindings[this.target.uuid]();
		}
		else {
			
			// 更新移动逻辑
			this.moveAction.update();
		}	
	}
}

class AttackTo extends Action {
	constructor(enity, target) {
		super();
		
		this.enity = enity;
		this.target = target;
		this.moveAction = new MoveTo(enity, enity.scene.findPath(enity.position, target.position));
	}
	
	update() {
		// 检查与目标点距离
		const xDiff = this.target.x - this.enity.x;
		const yDiff = this.target.y - this.enity.y;
		const distance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
		
		// 如果小于2个格子则进行对话
		if(distance < this.enity.scene.gridSize * 2) {		
			// 结束动作
			this.enity.state = `attack`;
			
			// 执行计算面对朝向
			const hude = xDiff == 0 && yDiff == 0 ? 0 :
				Math.atan2(xDiff, yDiff);			
			updateEnityDirection(this.enity, hude);
		}
		else {
			
			// 更新移动逻辑
			this.moveAction.update();
		}	
	}
}

/**
 * 直接攻击动作
 */
class Attack extends Action {
	constructor(enity) {
		this.enity = enity;
	}
	
	update() {
		// 向战场提供攻击扇形
		this.enity.scene.battle.skill();
	}
}

var characterAnimations = {};
function getCharacterAnimation(state) {
	if(!characterAnimations[state])
		characterAnimations[state] = L2D.Loader.load(`res/actions/${state}.ani`);
		
	return characterAnimations[state];
}

class Character extends PIXI.Container {
	
	constructor() {
		super();
		this.size = 32;
		this.frame = 0;
		this.speed = 1;
		this.direction = 0;
		this.equipments = [];
		this._lastState = "";
		this._lastFrameTime = new Date().getTime();
		
		// 基础状态
		this.hp = 100;
		this.sp = 100;
		this.atk = 20;
		this.def = 5;
		this.keep = 2000;
		this.range = 0.5;
		this.weight = 70; 
	}
	
	moveTo(target, callback) {
		this.action = new MoveTo(this, this.scene.findPath(this.position, target), callback);
	}
	
	attack() {
		this.action = new Attack(this);
	}
	
	attackTo(target, callback) {
		this.action = new AttackTo(this, target, callback);
	}
	
	talkTo(target, callback) {
		this.action = new TalkTo(this, target, callback);
	}
	
	isEnemy(target) {
		return true;
	}
	
	get centerPoint() {
		return {x:this.x,y:this.y};
	}
	
	set centerPoint(val) {
		this.x = val.x;
		this.y = val.y;
	}
	
	get state() {
		return this._lastState;
	}
	 
	set state (state) {
		// 加载对应动画包
		if(this._lastState != state) {
			this._lastState = state;
			this.frame = 0;
			this.animation = getCharacterAnimation(state);
		}
	}
	
	update() { 
		// 更新动画帧
		if(new Date().getTime() - this._lastFrameTime > 200) {
			this._lastFrameTime = new Date().getTime();
			this.frame++; 
			if(this.animation.Frames && 
				this.frame >= this.animation.Frames.length) 
				this.frame = 0; 
		}
		else return;
		
		// 更新当前目标
		if(!this.Properties) return;
		if(!this.animation || !this.animation.Frames) return;
		if(this.frame >= this.animation.Frames.length) return;
		
		var drawCalls = [];
		var frameInfo = this.animation.Frames[this.frame];
		for(var i = 0; i < frameInfo.Fragments.length; i++) {
		    var frag = frameInfo.Fragments[i];
		    drawCalls.push( 
		    {
		        location : frag.Locations[this.direction],
		        fragment : frag
		    });
		}
		
		drawCalls.sort(function(a,b) 
		{ 
		    return b.location.Z - a.location.Z;
		}); 
		
		while(this.children.length > 0)
			this.removeChildAt(0);
			
		for(var i = 0; i < drawCalls.length; i++) {
		    var dc = drawCalls[i];
		    var style = this.Properties.Style[dc.fragment.Folder];
		    if(style) {
		        var width = 0;
		        switch(dc.fragment.Folder) {
		            case "adornment": width = 18; break;
		            case "armor": width = 32; break;
		            case "body": width = 32; break;
		            case "bodyback": width = 32; break;
		            case "hand": width = 16; break;
		            case "head": width = 32; break;
		            case "leg": width = 16; break;
		            case "shoulder": width = 32; break; 
		            case "weapon": width = 64; break;
		            default: width = 32; break;
		        }
				
		        var halfWidth = width / 2;
		        var cc = dc.location;
		        var url = "res/role/" + dc.fragment.Folder + "/" + style;
		        var texture = PIXI.Texture.from(url);
				
				const gs = this.scene.gridSize / 2;
				const source = new PIXI.Texture(texture,  { x:width * cc.Image, y:0, width:width, height:width });
				const bunny = new PIXI.Sprite(source);
				bunny.x = cc.X - halfWidth;
				bunny.y = cc.Y - halfWidth - this.direction * 72 - 16 + gs;
				bunny.scale = this.scale;
				bunny.angle = this.angle;
				this.addChild(bunny);
		    }
		}
	}
}

function CharacterLoader(loader) {
    this.loader = loader;
}
CharacterLoader.prototype = {
    responseType : 'text',
    load : function(buffer, params, entry) {
        var jsonObj = JSON.parse(buffer);
        for(var member in jsonObj)
            entry[member] = jsonObj[member];

        return entry;
    },
    create : function() {
        return new Character(); 
    }
}

function AnimationLoader(loader) {
    this.loader = loader;
}
AnimationLoader.prototype = {
    responseType : 'text',
    load : function(buffer, params, entry) {
        var jsonObj = JSON.parse(buffer);
        for(var member in jsonObj)
            entry[member] = jsonObj[member];

        return entry;
    },
    create : function() {
        return {};
    }
}

L2D.Loader.addMode("act", new CharacterLoader(this));
L2D.Loader.addMode("ani", new AnimationLoader(this));